package org.jetlinks.pro.tenant;

import org.apache.commons.collections4.CollectionUtils;
import org.jetlinks.pro.assets.AssetBindManager;
import org.jetlinks.pro.assets.AssetBindRequest;
import org.jetlinks.pro.assets.AssetBinding;
import org.jetlinks.pro.assets.AssetPermission;
import org.jetlinks.pro.assets.AssetType;
import org.jetlinks.pro.assets.AssetUnbindRequest;
import org.jetlinks.pro.tenant.dimension.TenantDimensionType;
import org.jetlinks.pro.tenant.impl.BindAssetsRequest;
import org.jetlinks.pro.tenant.supports.DefaultTenantAsset;
import org.reactivestreams.Publisher;
import org.springframework.stereotype.Component;
import org.springframework.util.StringUtils;
import reactor.core.publisher.Flux;
import reactor.core.publisher.Mono;

import javax.annotation.Nonnull;
import java.util.Collection;
import java.util.List;
import java.util.stream.Collectors;

@Component
public class TenantAssetBindManager implements AssetBindManager {

    private final TenantAssetManager assetManager;

    public TenantAssetBindManager(TenantAssetManager assetManager) {
        this.assetManager = assetManager;
    }

    @Nonnull
    @Override
    public Mono<Integer> unbindAllAssets(@Nonnull String assetType, @Nonnull Collection<?> assetId) {

        return assetManager
            .unbindAllAssets(assetType, assetId);
    }

    @Nonnull
    @Override
    public Mono<Void> bindAssets(@Nonnull Publisher<AssetBindRequest> request) {
        return Flux
            .from(request)
            .filter(BindAssetsRequest.class::isInstance)
            .cast(BindAssetsRequest.class)
            .filter(r -> StringUtils.hasText(r.getTenantId()))
            .groupBy(BindAssetsRequest::getTenantId)
            .flatMap(group -> assetManager.bindAssets(String.valueOf(group.key()), false, group))
            .then();
    }

    @Nonnull
    @Override
    public Mono<Void> unbindAssets(@Nonnull Publisher<AssetUnbindRequest> requestFlux) {
        //租户需要单独使用AssetManager进行绑定
        return Mono.empty();
    }

    @Override
    public Mono<Void> changePermission(@Nonnull String targetType,
                                       @Nonnull Collection<String> targetIdList,
                                       @Nonnull AssetType assetType,
                                       @Nonnull Collection<?> assetIdList,
                                       @Nonnull AssetPermission... permissions) {
        return Mono.empty();
    }

    @Nonnull
    @Override
    public Flux<AssetBinding> getBindings(@Nonnull String targetType,
                                          @Nonnull Collection<String> targetIdList,
                                          @Nonnull AssetType assetType,
                                          @Nonnull Collection<?> assetIdList) {
        if (TenantDimensionType.any(targetType)) {
            return this
                .convertBinding(targetType, Flux
                    .fromIterable(targetIdList)
                    .flatMap(tenantId -> assetManager
                        .getTenantAssets(tenantId, assetType, null, assetIdList)
                    ))
                .cast(AssetBinding.class);
        }
        return Flux.empty();
    }

    @Nonnull
    @Override
    public Flux<AssetBinding> getBindings(@Nonnull Collection<String> targetTypes,
                                          @Nonnull AssetType assetType,
                                          @Nonnull Collection<?> assetIdList) {
        List<String> tenantType = targetTypes.stream().filter(TenantDimensionType::any).collect(Collectors.toList());
        if (CollectionUtils.isNotEmpty(tenantType)) {
            return this
                .convertBinding(tenantType.get(0),
                                assetManager
                                    .getTenantAssets(assetType, assetIdList))
                .cast(AssetBinding.class);
        }
        return Flux.empty();
    }

    @Nonnull
    @Override
    public Flux<AssetBinding> getBindings(@Nonnull String targetType,
                                          @Nonnull Collection<String> targetIdList,
                                          @Nonnull AssetType assetType) {
        if (TenantDimensionType.any(targetType)) {
            return this
                .convertBinding(targetType,
                                Flux.fromIterable(targetIdList)
                                    .flatMap(tenantId -> assetManager
                                        .getTenantAssets(tenantId, assetType, null)
                                    ))
                .cast(AssetBinding.class);
        }
        return Flux.empty();
    }

    private Flux<TenantAsset> convertBinding(String targetType, Flux<TenantAsset> flux) {
        //如果获取的绑定目标是租户，那只返回一个租户的资产数据
        if (TenantDimensionType.tenant.isSameType(targetType)) {
            return flux
                //根据租户ID和资产ID来去重
                .distinct(asset -> asset.getTenantId() + ":" + asset.getAssetId())
                .map(asset -> DefaultTenantAsset.of(
                    asset,
                    TenantDimensionType.tenant
                ));
        }
        return flux;
    }

    @Nonnull
    @Override
    public Flux<AssetBinding> getBindings(@Nonnull AssetType assetType,
                                          @Nonnull Collection<?> assetIdList) {
        return assetManager
            .getTenantAssets(assetType, assetIdList)
            .cast(AssetBinding.class);
    }
}
